package beanstao.util.collections.node;

import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;

import beanstao.util.Strings;
import beanstao.util.Util;

public class SimpleNode<T> implements Node<T>
{

	SimpleNode()
	{
	}

	private T obj;
	private Node<T> parent;
	private Node<T> prev;
	private Node<T> next;
	private Node<T> firstChild;
	private Node<T> lastChild;

	public T get()
	{
		return this.obj;
	}

	public Node<T> set(T obj)
	{
		this.obj = obj;
		return this;
	}

	public Node<T> parent()
	{
		return this.parent;
	}

	public Node<T> top()
	{
		if (null == this.parent)
		{
			return this;
		}
		return this.parent.top();
	}

	public Node<T> prev()
	{
		return this.prev;
	}

	public Node<T> prev(Node<T> node)
	{
		this.prev = node;
		return this;
	}

	public Node<T> next()
	{
		return this.next;
	}

	public Node<T> next(Node<T> node)
	{
		this.next = node;
		return this;
	}

	public boolean isRoot()
	{
		return null == this.parent;
	}

	public boolean isLast()
	{
		return null == this.next;
	}

	public boolean isFirst()
	{
		return null == this.prev;
	}

	public List<Node<T>> getAncestors()
	{
		List<Node<T>> list = new LinkedList<Node<T>>();
		Node<T> me = this.parent;
		while (me != null)
		{
			list.add(me);
			me = me.parent();
		}
		return list;
	}

	public int depth()
	{
		return this.getAncestors().size();
	}

	public List<Node<T>> getNextSibling()
	{
		List<Node<T>> list = new LinkedList<Node<T>>();
		Node<T> me = this.next;
		while (me != null)
		{
			list.add(me);
			me = me.next();
		}
		return list;
	}

	public List<Node<T>> getPrevSibling()
	{
		List<Node<T>> list = new LinkedList<Node<T>>();
		Node<T> me = this.prev;
		while (me != null)
		{
			list.add(me);
			me = me.prev();
		}
		return list;
	}

	public int index()
	{
		return this.getPrevSibling().size();
	}

	public List<Node<T>> getChildren()
	{
		List<Node<T>> list = new LinkedList<Node<T>>();
		if (null != this.firstChild)
		{
			list.add(this.firstChild);
			list.addAll(this.firstChild.getNextSibling());
		}
		return list;
	}

	public int countChildren()
	{
		int re = 0;
		if (null != this.firstChild)
		{
			Node<T> me = this.firstChild;
			while (me != null)
			{
				re++;
				me = me.next();
			}
		}
		return re;
	}

	public boolean hasChild()
	{
		return null != this.firstChild;
	}

	public Node<T> firstChild()
	{
		return this.firstChild;
	}

	public Node<T> lastChild()
	{
		return this.lastChild;
	}

	public Node<T> parent(Node<T> node)
	{
		this.parent = node;
		return this;
	}

	@SuppressWarnings("unchecked")
	public Node<T> add(Node<?>... nodes)
	{
		if (nodes.length == 0)
		{
			return this;
		}
		if (nodes.length == 1)
		{
			Node<T> node = (Node<T>) nodes[0];
			node.parent(this);
			if (!this.hasChild())
			{
				this.firstChild = node;
				this.lastChild = node;
				node.next(null);
				node.prev(null);
			}
			else
			{
				this.lastChild.next(node);
				node.prev(this.lastChild);
				node.next(null);
				this.lastChild = node;
			}
		}
		else
		{
			this.firstChild = (Node<T>) nodes[0];
			this.firstChild.parent(this);
			this.firstChild.next((Node<T>) nodes[1]);
			int i = 1;
			for (; i < nodes.length - 1; i++)
			{
				Node<T> node = (Node<T>) nodes[i];
				node.parent(this);
				node.prev((Node<T>) nodes[i - 1]);
				node.next((Node<T>) nodes[i + 1]);
			}
			this.lastChild = (Node<T>) nodes[i];
			this.lastChild.parent(this);
			this.lastChild.prev((Node<T>) nodes[i - 1]);

		}
		return this;
	}

	public Node<T> addFirst(Node<T> node)
	{
		node.parent(this);
		if (!this.hasChild())
		{
			this.firstChild = node;
			this.lastChild = node;
			node.next(null);
			node.prev(null);
		}
		else
		{
			this.firstChild.prev(node);
			node.next(this.firstChild);
			node.prev(null);
			this.firstChild = node;
		}
		return this;
	}

	public Node<T> child(int index)
	{
		if (this.hasChild())
		{
			return this.firstChild.next(index);
		}
		return null;
	}

	public Node<T> desc(int... indexes)
	{
		Node<T> me = this;
		for (int i : indexes)
		{
			if (!me.hasChild())
			{
				return null;
			}
			me = me.firstChild().next(i);
		}
		return me;
	}

	public Node<T> next(int index)
	{
		if (index < 0)
		{
			return null;
		}
		Node<T> me = this;
		while ((index > 0) && (me != null))
		{
			index--;
			me = me.next();
		}
		if (index > 0)
		{
			return null;
		}
		return me;
	}

	public Node<T> prev(int index)
	{
		Node<T> me = this;
		while ((index > 0) && (me != null))
		{
			index--;
			me = me.prev();
		}
		return me;
	}

	public Node<T> insertBefore(int index, Node<T> node)
	{
		Node<T> me = this.child(index);
		if (null != me)
		{
			node.next(me);
			node.prev(me.prev());
			me.prev().next(node);
			me.prev(node);
			node.parent(this);
			if (this.firstChild == me)
			{
				this.firstChild = node;
			}
		}
		return this;
	}

	public Node<T> pop()
	{
		if (!this.hasChild())
		{
			return null;
		}
		Node<T> re = this.lastChild;
		this.lastChild = this.lastChild.prev();
		if (null == this.lastChild)
		{
			this.firstChild = null;
		}
		else
		{
			this.lastChild.next(null);
		}
		return re.prev(null).next(null);
	}

	public Node<T> popFirst()
	{
		if (!this.hasChild())
		{
			return null;
		}
		Node<T> re = this.firstChild;
		this.firstChild = this.firstChild.next();
		if (null == this.firstChild)
		{
			this.lastChild = null;
		}
		else
		{
			this.firstChild.prev(null);
		}
		return re.prev(null).next(null);
	}

	public Node<T> remove(int index)
	{
		if (this.hasChild())
		{
			Node<T> node = this.child(index);
			if (null == node)
			{
				return null;
			}
			else if (node.isLast())
			{
				return this.pop();
			}
			else if (node.isFirst())
			{
				return this.popFirst();
			}
			node.next().prev(node.prev());
			node.prev().next(node.next());
			return node.prev(null).next(null);
		}
		return null;
	}

	@Override
	public String toString()
	{
		StringBuilder sb = new StringBuilder();
		appendTo(this, sb, 0);
		return sb.toString();
	}

	static void appendTo(Node<?> node, StringBuilder sb, int depth)
	{
		sb.append(Strings.dup("    ", depth)).append(node.get() == null ? "NULL" : node.get().toString());
		Node<?> chd = node.firstChild();
		while (chd != null)
		{
			sb.append('\n');
			appendTo(chd, sb, depth + 1);
			chd = chd.next();
		}
	}

	static class InnerIterator<T> implements Iterator<Node<T>>
	{

		private final Node<T> root;
		private Node<T> node;

		InnerIterator(Node<T> node)
		{
			this.root = node;
			if (this.root.hasChild())
			{
				this.node = this.root.child(0);
			}
			else
			{
				this.node = this.root;
			}
		}

		public boolean hasNext()
		{
			return this.node != this.root;
		}

		public Node<T> next()
		{
			if (this.node == this.root)
			{
				return null;
			}
			Node<T> re = this.node;
			if (this.node.hasChild())
			{
				this.node = this.node.firstChild();
			}
			else if (!this.node.isLast())
			{
				this.node = this.node.next();
			}
			else
			{
				while (this.node.isLast() && !this.node.isRoot())
				{
					this.node = this.node.parent();
				}
				if (!this.node.isRoot())
				{
					this.node = this.node.next();
				}
			}
			return re;
		}

		public void remove()
		{
			throw Util.makeThrow("No implement yet!");
		}

	}

	public Iterator<Node<T>> iterator()
	{
		return new InnerIterator<T>(this);
	}

}
